package com.leilei;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.thread.ThreadFactoryBuilder;
import cn.hutool.json.JSONUtil;
import lombok.extern.log4j.Log4j2;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
 * @author lei
 * @create 2023-02-27 15:30
 * @desc
 **/
@Log4j2
public class CollectCalc {

    private CollectCalc() {
    }

    /**
     * 清理阈值 计算定位数据点或已有区域达到阈值倍数触发一次清理区域
     */
    public static final int POINT_CLEAN_THRESHOLD = 15000;
    public static final int AREA_CLEAN_THRESHOLD = 10000;
    public static final int AREA_SPLIT_THRESHOLD = 300;
    public static final int CORE = Runtime.getRuntime().availableProcessors();
    /**
     * 区域合并时 半径判断阈值
     */
    public static final int AREA_MERGE_SPLIT_THRESHOLD = 1000;

    public static final ThreadPoolExecutor DEFAULT_CALC_EXECUTOR = new ThreadPoolExecutor(CORE, CORE + 2, 10, TimeUnit.MINUTES,
            new ArrayBlockingQueue<>(20480), new ThreadFactoryBuilder().setNamePrefix("default-collect-calc-")
            .setUncaughtExceptionHandler((t, e) -> log.error("线程:{}处理异常", t.getName(), e))
            .build(), new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * 聚集区计算 使用默认线程池
     *
     * @param locationBaseList 定位点数据
     * @param collectDistance  聚集距离 m
     * @param extendDistance   扩展距离 m
     * @return List<PolygonMapAreaVO>
     * @author lei
     * @date 2023-03-22 11:42:31
     */
    public static <V> List<CollectPolygonAreaVO<V>> calc(List<LocationBase<V>> locationBaseList, Double collectDistance, Double extendDistance) {
        return collectCalc(locationBaseList, collectDistance, extendDistance, DEFAULT_CALC_EXECUTOR);
    }


    /**
     * 聚集区计算 使用自己的线程池
     *
     * @param locationBaseList 定位点数据
     * @param collectDistance  聚集距离 m
     * @param extendDistance   扩展距离 m
     * @param executor         执行线程池
     * @return List<PolygonMapAreaVO>
     * @author lei
     * @date 2023-04-07 10:07:40
     */
    public static <V> List<CollectPolygonAreaVO<V>> calc(List<LocationBase<V>> locationBaseList, Double collectDistance, Double extendDistance,
                                                         Executor executor) {
        return collectCalc(locationBaseList, collectDistance, extendDistance, executor);
    }

    /**
     * 区域聚集计算
     *
     * @param locationBaseList 定位点数据
     * @param collectDistance  聚集距离 m
     * @param extendDistance   扩展距离 m
     * @param executor
     * @return List<PolygonMapAreaVO>
     * @author lei
     * @date 2023-04-07 10:07:17
     */
    private static <V> List<CollectPolygonAreaVO<V>> collectCalc(List<LocationBase<V>> locationBaseList, Double collectDistance, Double extendDistance,
                                                                 Executor executor) {
        long start = System.currentTimeMillis();
        if (CollUtil.isEmpty(locationBaseList)) {
            return new ArrayList<>();
        }
        List<CollectPolygonArea<V>> collectPolygonAreas = new ArrayList<>();
        AtomicLong incr = new AtomicLong();
        AtomicLong incrId = new AtomicLong();
        Set<LocationBase<V>> locationBaseSet = new HashSet<>(locationBaseList);
        log.debug("输入,源定位数据点数量：{} 去重后数据量：{}", locationBaseList.size(), locationBaseSet.size());
        for (LocationBase<V> locationBase : locationBaseSet) {
            forkJoinPolygonArea(collectPolygonAreas, locationBase, collectDistance, incr, incrId, executor);
        }
        List<CollectPolygonAreaVO<V>> voList = collectPolygonAreas.parallelStream()
                .filter(CollectPolygonArea::isLive)
                .filter(x -> x.getPoints().getSize() >= CollectCalcUtil.MIN_POLYGON_SIZE)
                .map(x -> x.po2vo(extendDistance))
                .sorted(Comparator.comparing(x -> x.getLocationBases().size(), Comparator.reverseOrder()))
                .collect(toList());
        log.debug("输出,共绘制定边点>=3条的多边形区域:{}个 耗时:{}ms", voList.size(), System.currentTimeMillis() - start);
        return voList;
    }


    /**
     * 加入多边形计算
     *
     * @param collectPolygonAreas 现有聚集区
     * @param curLocationBase     当前报警
     * @param collectDistance     聚集距离
     * @param calcIncr            累计计算量
     * @param incrId
     * @param calcExecutor
     * @return void
     * @author lei
     * @date 2023-03-08 11:24:30
     */
    private static <V> void forkJoinPolygonArea(List<CollectPolygonArea<V>> collectPolygonAreas, LocationBase<V> curLocationBase, Double collectDistance,
                                                AtomicLong calcIncr, AtomicLong incrId, Executor calcExecutor) {
        long calcNum = calcIncr.incrementAndGet();
        if (CollUtil.isEmpty(collectPolygonAreas)) {
            initPolygonArea(curLocationBase, collectPolygonAreas, incrId);
            return;
        }
        boolean needInitNewPolygon;
        List<List<CollectPolygonArea<V>>> splitAreaList = splitPolygonArea(collectPolygonAreas);
        int splitAreaSize = splitAreaList.size();
        if (splitAreaSize > 1) {
            List<CompletableFuture<Boolean>> collect = splitAreaList.stream().map(areas ->
                    CompletableFuture.supplyAsync(() -> collectCalc(areas, curLocationBase, collectDistance), calcExecutor)).collect(toList());
            List<Boolean> forkResultList = collect.stream().map(CompletableFuture::join).collect(toList());
            // 分支结果全真为真,非全真数量大于1则需再次合并
            long forkNeedInitSize = forkResultList.stream().filter(x -> x).count();
            needInitNewPolygon = splitAreaSize == forkNeedInitSize;
            if (Boolean.FALSE.equals(needInitNewPolygon) && splitAreaSize - forkNeedInitSize > 1) {
                forkMergePolygonArea(collectPolygonAreas, collectDistance);
            }
        } else {
            needInitNewPolygon = collectCalc(collectPolygonAreas, curLocationBase, collectDistance);
        }
        if (needInitNewPolygon) {
            initPolygonArea(curLocationBase, collectPolygonAreas, incrId);
        }
        cleanDispersedArea(collectPolygonAreas, calcNum);
    }


    /**
     * 合并线程子分支区域计算结果
     *
     * @return List<PolygonMapArea> 返回分支合并后的区域列表,此时的区域列表为当前定位点计算后的最新结果
     * @author lei
     * @date 2023-03-27 15:46:26
     */
    private static <V> void forkMergePolygonArea(List<CollectPolygonArea<V>> collectPolygonAreas, Double collectDistance) {
        CollectPolygonArea<V> belongArea = null;
        for (CollectPolygonArea<V> collectPolygonArea : collectPolygonAreas) {
            if (collectPolygonArea.isBelong() && collectPolygonArea.isLive()) {
                if (belongArea == null) {
                    belongArea = collectPolygonArea;
                } else {
                    mergePolygonArea(belongArea, collectPolygonArea, collectDistance);
                }
            }
        }
    }


    /**
     * 拆分多边形区域，这里使用原集合子视图
     *
     * @param collectPolygonAreas
     * @return List<List < PolygonMapArea>>
     * @author lei
     * @date 2023-03-27 14:41:25
     */
    private static <V> List<List<CollectPolygonArea<V>>> splitPolygonArea(List<CollectPolygonArea<V>> collectPolygonAreas) {
        List<List<CollectPolygonArea<V>>> result = new ArrayList<>();
        int size = collectPolygonAreas.size();
        if (size <= AREA_SPLIT_THRESHOLD) {
            result.add(collectPolygonAreas);
        } else {
            int batchSize = (int) Math.ceil((double) size / CORE);
            for (int i = 0; i < size; i += batchSize) {
                result.add(collectPolygonAreas.subList(i, Math.min(size, i + batchSize)));
            }
        }
        return result;
    }

    /**
     * 聚合计算
     *
     * @param collectPolygonAreas 之前聚合的区域
     * @param curLocationBase     报警数据
     * @param collectDistance     聚合距离
     * @return boolean
     * @author lei
     * @date 2023-03-24 15:55:31
     */
    private static <V> boolean collectCalc(List<CollectPolygonArea<V>> collectPolygonAreas, LocationBase<V> curLocationBase, Double collectDistance) {
        // 是否需要初始化新多边形
        boolean needInitNewPolygon = true;
        CollectPolygonArea<V> belongedArea = null;
        MapPoint curPoint = new MapPoint(curLocationBase.getLongitude(), curLocationBase.getLatitude()/*, curLocationBase.getPointName()*/);
        for (CollectPolygonArea<V> collectPolygonArea : collectPolygonAreas) {
            if (!collectPolygonArea.isLive()) {
                continue;
            }
            // 先算圆心到当前点距离是否超过指定阈值,超过则进行下一个多边形判断
            double distance = CollectCalcUtil.distance(collectPolygonArea.getCycleCenter(), curPoint);
            if (distance > collectPolygonArea.getRadius() + collectDistance) {
                continue;
            }
            if (collectPolygonArea.getPoints().getSize() == 1) {
                needInitNewPolygon = false;
                if (belongedArea == null) {
                    // 归属当前聚集点，计算最大经纬度与圆心
                    collectPolygonArea.getLocationBases().add(curLocationBase);
                    polygonDrawCircle(curPoint, collectPolygonArea, distance);
                    collectPolygonArea.setBelong(true);
                    belongedArea = collectPolygonArea;
                } else {
                    mergePolygonArea(belongedArea, collectPolygonArea, collectDistance);
                }
                continue;
            }
            // 属于当前多边形扫描圆距离,则进行多边形内外部判断
            boolean inPolygonArea = CollectCalcUtil.locateInPolygonArea(curPoint, collectPolygonArea);
            if (inPolygonArea) {
                // 当前点在现多边形内
                needInitNewPolygon = false;
                if (belongedArea == null) {
                    collectPolygonArea.getLocationBases().add(curLocationBase);
                    collectPolygonArea.setBelong(true);
                    belongedArea = collectPolygonArea;
                } else {
                    mergePolygonArea(belongedArea, collectPolygonArea, collectDistance);
                }
                continue;
            }
            // 当前点在多边形之外,则与各边进行判断,找到与其相邻最近边,如距离小于指定阈值则加入多边形
            Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdge = getNearestEdge(curPoint, collectPolygonArea.getPoints());
            if (nearestEdge == null) {
                log.error("当前点:{}未找到最近边", curLocationBase);
                continue;
            }
            Pair<Node<MapPoint>, Node<MapPoint>> nearestNode = nearestEdge.getKey();
            double nearestEdgeDistance = nearestEdge.getValue();
            if (nearestEdgeDistance <= collectDistance) {
                needInitNewPolygon = false;
                if (belongedArea == null) {
                    collectPolygonArea.getLocationBases().add(curLocationBase);
                    if (nearestEdgeDistance > CollectCalcUtil.ON_LINE_DISTANCE) {
                        // 加入多边形并削内凹角
                        tryRecalculationArea(collectPolygonArea, nearestNode.getKey(), nearestNode.getValue(), curPoint);
                    }
                    collectPolygonArea.setBelong(true);
                    belongedArea = collectPolygonArea;
                } else {
                    mergePolygonArea(belongedArea, collectPolygonArea, collectDistance);
                }
            }
        }
        return needInitNewPolygon;
    }


    /**
     * 清理较为离散的多边形区域
     * 一定阈值定位点时触发清理,一定阈值聚集区时触发清理
     *
     * @param collectPolygonAreas
     * @param calcNum
     * @return void
     * @author lei
     * @date 2023-03-23 11:08:10
     */
    private static <V> void cleanDispersedArea(List<CollectPolygonArea<V>> collectPolygonAreas, long calcNum) {
        for (CollectPolygonArea<V> collectPolygonArea : collectPolygonAreas) {
            collectPolygonArea.setBelong(false);
        }
        if (calcNum % POINT_CLEAN_THRESHOLD == 0) {
            int beforeSize = collectPolygonAreas.size();
            long start = System.currentTimeMillis();
            collectPolygonAreas.removeIf(x -> !x.isLive() || x.getPoints().getSize() == 1);
            log.debug("当前已计算定位点数量：{} 触发清理区域,原区域数量：{} 清理后区域数量：{} 清理耗时：{}ms", calcNum, beforeSize, collectPolygonAreas.size(), System.currentTimeMillis() - start);
        }
        if (collectPolygonAreas.size() % AREA_CLEAN_THRESHOLD == 0) {
            int beforeSize = collectPolygonAreas.size();
            long start = System.currentTimeMillis();
            collectPolygonAreas.removeIf(x -> !x.isLive() || x.getPoints().getSize() < CollectCalcUtil.MIN_POLYGON_SIZE);
            log.debug("当前已计算出聚集区数量：{} 触发清理区域内聚集点小于3点数据,清理后区域数量：{} 清理耗时：{}ms", beforeSize, collectPolygonAreas.size(), System.currentTimeMillis() - start);
        }
    }


    /**
     * 构建最新的区域 区域2数据转移至区域1
     *
     * @param area1           保留的区域
     * @param area2           需转移合并的区域 area2->area1
     * @param collectDistance
     * @author lei
     * @date 2023-03-08 10:56:08
     */
    private static <V> void mergePolygonArea(CollectPolygonArea<V> area1, CollectPolygonArea<V> area2, Double collectDistance) {
        // if (area1.getRadius() >= AREA_MERGE_SPLIT_THRESHOLD) {
        //     // 计算它们之间的最短距离
        //     Coordinate[] coordinates = area1.getPoints().getList().stream().map(x -> new Coordinate(x.getLongitude(), x.getLatitude())).toArray(Coordinate[]::new);
        //     Geometry geometry = new ConvexHull(coordinates, new GeometryFactory()).getConvexHull();
        //     Coordinate[] coordinates2 = area2.getPoints().getList().stream().map(x -> new Coordinate(x.getLongitude(), x.getLatitude())).toArray(Coordinate[]::new);
        //     Geometry geometry2 = new ConvexHull(coordinates2, new GeometryFactory()).getConvexHull();
        //     double minDistance = DistanceOp.distance(geometry, geometry2);
        //     // 聚合距离*10 单位为米转换后数据
        //     if (minDistance >= collectDistance * 10) {
        //         log.info("area1:{}和area2:{},最短距离：{}不合并", area1.getId(), area2.getId(), minDistance);
        //         area2.setLive(true);
        //         return;
        //     }
        // }
        area2.setLive(false);
        List<MapPoint> area2PointList = area2.getPoints().getList();
        for (MapPoint mapPoint : area2PointList) {
            pointTryJoinArea(area1, mapPoint);
        }
        area1.getLocationBases().addAll(area2.getLocationBases());
    }

    /**
     * 尝试将当前点加入区域组成节点
     *
     * @param curPolygon
     * @param mapPoint
     * @return void
     * @author lei
     * @date 2023-03-08 14:49:10
     */
    private static <V> void pointTryJoinArea(CollectPolygonArea<V> curPolygon, MapPoint mapPoint) {
        LinkList<MapPoint> points = curPolygon.getPoints();
        if (points.getSize() == 1) {
            double distance = CollectCalcUtil.distance(mapPoint, points.getFirst().getData());
            polygonDrawCircle(mapPoint, curPolygon, distance);
            return;
        }
        Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdge = getNearestEdge(mapPoint, points);
        Double nearestEdgeDistance = nearestEdge.getValue();
        if (nearestEdgeDistance > CollectCalcUtil.ON_LINE_DISTANCE) {
            Pair<Node<MapPoint>, Node<MapPoint>> nearestNode = nearestEdge.getKey();
            tryRecalculationArea(curPolygon, nearestNode.getKey(), nearestNode.getValue(), mapPoint);
        }
    }


    /**
     * 第二个点加入,尝试组合多边形（线）,并绘制虚拟圆
     *
     * @param curPoint              当前聚集点
     * @param curCollectPolygonArea 当前要加入的区域
     * @param distance              距离
     * @return void
     * @author lei
     * @date 2023-03-02 16:48:51
     */
    private static <V> void polygonDrawCircle(MapPoint curPoint, CollectPolygonArea<V> curCollectPolygonArea, double distance) {
        // 如果第二个点与第一个点相差距离小于指定,则其不成为多边形定边点,只加明细
        if (distance <= CollectCalcUtil.ON_LINE_DISTANCE) {
            return;
        }
        MapPoint data = curCollectPolygonArea.getPoints().getFirst().getData();
        curCollectPolygonArea.getPoints().add(curPoint);
        MapPoint midpoint = CollectCalcUtil.getMidpoint(curPoint, data);
        curCollectPolygonArea.setCycleCenter(midpoint);
        curCollectPolygonArea.setRadius(distance / 2);
        curCollectPolygonArea.setMaxLatitude(Math.max(curPoint.getLatitude(), data.getLatitude()));
        curCollectPolygonArea.setMaxLongitude(Math.max(curPoint.getLongitude(), data.getLongitude()));
        curCollectPolygonArea.setMinLatitude(Math.min(curPoint.getLatitude(), data.getLatitude()));
        curCollectPolygonArea.setMinLongitude(Math.min(curPoint.getLongitude(), data.getLongitude()));
    }


    /**
     * 根据当前点 获取与当前多边形最近边
     *
     * @param curPoint 当前点
     * @param points   当前多边形组合点
     * @return Pair<Pair < Node < MapPoint>,Node<MapPoint>>,Double> 最近边组成点 当前点与最近边距离
     * @author lei
     * @date 2023-03-02 15:45:18
     */
    private static Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> getNearestEdge(MapPoint curPoint, LinkList<MapPoint> points) {
        Node<MapPoint> foreachNode = points.getFirst();
        long pointsSize = points.getSize();
        Double nearestEdgeDistance = null;
        List<Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double>> pairList = new ArrayList<>();
        for (long i = 0L; i < pointsSize; i++) {
            Node<MapPoint> endPoint = foreachNode.getNext();
            double curDistance = CollectCalcUtil.pointToLine(foreachNode.getData(), endPoint.getData(), curPoint);
            if (nearestEdgeDistance == null || nearestEdgeDistance >= curDistance) {
                nearestEdgeDistance = curDistance;
                Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdge = new Pair<>(Pair.of(foreachNode, foreachNode.getNext()), curDistance);
                pairList.add(nearestEdge);
            }
            foreachNode = endPoint;
        }
        Map<Double, List<Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double>>> map = pairList.stream().collect(Collectors.groupingBy(Pair::getValue));
        List<Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double>> nearestEdgeList = map.get(nearestEdgeDistance);
        // 特殊情况会出现当前点d与多边形两个边 (顺时针查找ab bc、或逆时针查找 ab ca)距离相同情况,因此做特殊处理,返回其夹角度数最小所在的那一边
        if (pointsSize >= CollectCalcUtil.MIN_POLYGON_SIZE && nearestEdgeList.size() == 2) {
            // 边1
            Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdgeOne = nearestEdgeList.get(0);
            Pair<Node<MapPoint>, Node<MapPoint>> edgeOne = nearestEdgeOne.getKey();
            Node<MapPoint> edgeOneStart = edgeOne.getKey();
            Node<MapPoint> edgeOneEnd = edgeOne.getValue();

            // 边2
            Pair<Pair<Node<MapPoint>, Node<MapPoint>>, Double> nearestEdgeTwo = nearestEdgeList.get(1);
            Pair<Node<MapPoint>, Node<MapPoint>> edgeTwo = nearestEdgeTwo.getKey();
            Node<MapPoint> edgeTwoStart = edgeTwo.getKey();
            Node<MapPoint> edgeTwoEnd = edgeTwo.getValue();
            double cosValue1;
            double cosValue2;
            // 顺时针相邻
            if (edgeTwoStart.equals(edgeOneEnd)) {
                // 求 边1 开始 结束 当前点组成夹角余弦 （a b d）
                cosValue1 = CollectCalcUtil.cosValue(edgeOneStart.getData(), edgeOneEnd.getData(), curPoint);
                // 求 边2 结束 开始 当前点组成夹角余弦  (c b d)
                cosValue2 = CollectCalcUtil.cosValue(edgeTwoEnd.getData(), edgeTwoStart.getData(), curPoint);
            } else {
                // a b c a
                // 求 边1 结束 开始 当前点组成夹角余弦 （b a d）
                cosValue1 = CollectCalcUtil.cosValue(edgeOneEnd.getData(), edgeOneStart.getData(), curPoint);
                // 求 边2 开始 结束 当前点组成夹角余弦  (c a d)
                cosValue2 = CollectCalcUtil.cosValue(edgeTwoStart.getData(), edgeTwoEnd.getData(), curPoint);
            }
            // 返回小夹角所在边,如夹角一致则交由下一步消内凹逻辑
            return cosValue1 >= cosValue2 ? nearestEdgeOne : nearestEdgeTwo;

        }
        return nearestEdgeList.get(0);
    }

    /**
     * 初始化多边形区域
     *
     * @param locationBase
     * @param collectPolygonAreas
     * @param incrId
     * @return void
     * @author lei
     * @date 2023-02-28 15:18:18
     */
    private static <V> void initPolygonArea(LocationBase<V> locationBase, List<CollectPolygonArea<V>> collectPolygonAreas, AtomicLong incrId) {
        MapPoint curPoint = new MapPoint(locationBase.getLongitude(), locationBase.getLatitude()/*, locationBase.getPointName()*/);
        CollectPolygonArea<V> collectPolygonArea = new CollectPolygonArea<>();
        collectPolygonArea.setLive(true);
        collectPolygonArea.setId(incrId.incrementAndGet());
        LinkList<MapPoint> points = new LinkList<>();
        points.add(curPoint);
        collectPolygonArea.setPoints(points);
        collectPolygonArea.setCycleCenter(curPoint);
        collectPolygonArea.setRadius(0.0);
        collectPolygonArea.setMaxLatitude(curPoint.getLatitude());
        collectPolygonArea.setMaxLongitude(curPoint.getLongitude());
        collectPolygonArea.setMinLatitude(curPoint.getLatitude());
        collectPolygonArea.setMinLongitude(curPoint.getLongitude());
        Set<LocationBase<V>> locationBases = new HashSet<>();
        locationBases.add(locationBase);
        collectPolygonArea.setLocationBases(locationBases);
        collectPolygonAreas.add(collectPolygonArea);
    }

    /**
     * 重新构建多边形区域
     *
     * @param collectPolygonArea 多边形区域
     * @param foreachNode        当前遍历节点
     * @param nextNode           当前遍历节点下个节点
     * @param curPoint           当前新加入的点
     * @return void
     * @author lei
     * @date 2023-02-28 15:18:28
     */
    public static <V> void tryRecalculationArea(CollectPolygonArea<V> collectPolygonArea, Node<MapPoint> foreachNode, Node<MapPoint> nextNode,
                                                MapPoint curPoint) {
        // 添加点之后 需要重新设置圆心、最大最小经纬度
        CollectCalcUtil.reviseArea(collectPolygonArea, curPoint);
        LinkList<MapPoint> points = collectPolygonArea.getPoints();
        if (points.getSize() == 2) {
            // 当前区域为两个点则计算一下中间点 是否在开头结尾点线段上,如在则消除中间点
            points.add(curPoint);
            double pointToLine = CollectCalcUtil.pointToLine(foreachNode.getData(), curPoint, nextNode.getData());
            if (pointToLine <= CollectCalcUtil.ON_LINE_DISTANCE) {
                points.del(nextNode);
            }
            return;
        }
        points.add(foreachNode, curPoint);
        CollectCalcUtil.removeConcavePoints(collectPolygonArea);

    }


    public static void testCollect() {
        List<LocationBase<Integer>> locationBases = new LinkedList<>();
        locationBases.add(new LocationBase<>(1, 104063037, 30701011/*, "九里堤"*/));
        locationBases.add(new LocationBase<>(2, 104072595, 30700887/*, "北站西"*/));
        locationBases.add(new LocationBase<>(4, 104064726, 30699334/*, "九里堤农贸"*/));
        locationBases.add(new LocationBase<>(3, 104064007, 30692037/*, "西北桥"*/));
        locationBases.add(new LocationBase<>(5, 104073386, 30702136/*, "城北派出所"*/));
        locationBases.add(new LocationBase<>(6, 104078345, 30692168/*, "人民北路"*/));
        locationBases.add(new LocationBase<>(9, 104052828, 30699939/*, "哈啰酒店-成都西南交大店"*/));
        locationBases.add(new LocationBase<>(10, 104052936, 30698899/*, "矮冬瓜红茶火锅"*/));
        locationBases.add(new LocationBase<>(11, 104052199, 30698231/*, "沈跃全中医"*/));
        locationBases.add(new LocationBase<>(7, 104067169, 30695763/*, "五丁小学"*/));
        locationBases.add(new LocationBase<>(8, 104061456, 30700576/*, "那屋网客"*/));

        // 这里是测试多个多边形组合的测试点
        locationBases.add(new LocationBase<>(12, 104057823, 30698402/*, "金牛区政府"*/));

        System.out.println(JSONUtil.toJsonStr(locationBases));
        List<CollectPolygonAreaVO<Integer>> calc = calc(locationBases, 900.0D, 200.0D);
        System.out.println(JSONUtil.toJsonStr(calc));
    }

    public static void main(String[] args) {
        testCollect();
    }
}
